﻿using System;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Serialization.Formatters.Binary;
using System.Media;

namespace wav文件提取转换
{
    /// <summary>
    /// 主窗体类
    /// </summary>
    public partial class MainPage : Form
    {
        // 创建播放器
        SoundPlayer splayer = new SoundPlayer();

        /// <summary>
        /// 构造函数
        /// </summary>
        public MainPage()
        {
            // 初始化组件
            InitializeComponent();
        }

        /// <summary>
        /// 主窗体加载事件处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MainPage_Load(object sender, EventArgs e)
        {
            // 禁止放缩
            MinimumSize = MaximumSize = Size;
            // 不能最大化
            MaximizeBox = false;
            // 清空信息栏
            listDetails.Items.Clear();
            // 按键不可用
            btnDeleteFile.Enabled = false;
            btnExportData.Enabled = false;
            btnPlaySound.Enabled = false;
            btnClearFiles.Enabled = false;
            btnExportAllData.Enabled = false;
        }

        /// <summary>
        /// 添加文件按键按下事件处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnAddFiles_Click(object sender, EventArgs e)
        {
            // 创建对话框对象
            OpenFileDialog dlg = new OpenFileDialog();
            // 设置对话框选项
            dlg.Multiselect = true;                 // 可以选择多个文件
            dlg.DefaultExt = ".wav";                // 文件扩展名.wav
            dlg.Filter = "Windows波形文件|*.wav";   // 文件过滤器
            // 打开对话框
            if (dlg.ShowDialog() == DialogResult.OK)
            {
                // 清除之前选中
                listFiles.SelectedItems.Clear();
                // 建立已有数据查询表
                string[] last_files = new string[listFiles.Items.Count];
                int i = 0;
                foreach (ListViewItem item in listFiles.Items)
                {
                    last_files[i++] = item.SubItems[1].Text + "\\" + item.SubItems[0].Text;
                }
                // 遍历目录文件
                foreach (string file in dlg.FileNames)
                {
                    // 查询是否已经添加了
                    int index = last_files.ToList().IndexOf(file);
                    if (index < 0)
                    {
                        // 加入文件到列表
                        ListViewItem item = new ListViewItem();
                        item.Text = Path.GetFileName(file);
                        item.SubItems.Add(Path.GetDirectoryName(file));
                        item.Selected = true;
                        listFiles.Items.Add(item);
                    }
                    else
                    {
                        listFiles.Items[index].Selected = true;
                    }
                }
                // 获得焦点
                listFiles.Focus();
            }
        }

        /// <summary>
        /// 显示信息
        /// </summary>
        /// <param name="details"></param>
        private void listDetails_DisplayInfo(string path, WavFileDetails details)
        {
            ListViewItem item;
            listDetails.Items.Clear();

            item = new ListViewItem("文件名");
            item.SubItems.Add(path);
            listDetails.Items.Add(item);

            item = new ListViewItem("文件大小(bytes)");
            item.SubItems.Add(details.header.riff.fileSize.ToString());
            listDetails.Items.Add(item);

            item = new ListViewItem("编码格式");
            if (details.header.fmt.wFormatag == 1)
            {
                item.SubItems.Add(details.header.fmt.wFormatag.ToString() + "-PCM");
            }
            else
            {
                item.SubItems.Add(details.header.fmt.wFormatag.ToString() + "-ADPCM");
            }
            listDetails.Items.Add(item);

            item = new ListViewItem("声道");
            item.SubItems.Add(details.header.fmt.nChannls.ToString());
            listDetails.Items.Add(item);

            item = new ListViewItem("采样率(Hz)");
            item.SubItems.Add(details.header.fmt.nSamplesPerSec.ToString());
            listDetails.Items.Add(item);

            item = new ListViewItem("采样位数(bits)");
            item.SubItems.Add(details.header.fmt.wBitsPerSample.ToString());
            listDetails.Items.Add(item);
        }

        /// <summary>
        /// 绘制标题栏
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void listView_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e)
        {
            if (e.ColumnIndex == 0)
            {
                e.Graphics.FillRectangle(Brushes.DarkGray, e.Bounds);   //采用特定颜色绘制标题列,这里我用的灰色
                e.DrawText();   //采用默认方式绘制标题文本
            }

            else if (e.ColumnIndex == 1)
            {
                e.Graphics.FillRectangle(Brushes.DarkGray, e.Bounds);   //采用特定颜色绘制标题列,这里我用的灰色
                e.DrawText();   //采用默认方式绘制标题文本
            }

            else if (e.ColumnIndex == 2)
            {
                e.Graphics.FillRectangle(Brushes.DarkGray, e.Bounds);   //采用特定颜色绘制标题列,这里我用的灰色
                e.DrawText();   //采用默认方式绘制标题文本
            }
        }

        /// <summary>
        /// 绘制项
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void listView_DrawItem(object sender, DrawListViewItemEventArgs e)
        {
            e.DrawDefault = true; //采用系统默认方式绘制项
        }

        /// <summary>
        /// 绘制子项
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void listView_DrawSubItem(object sender, DrawListViewSubItemEventArgs e)
        {
            e.DrawDefault = true; //采用系统默认方式绘制项
        }

        /// <summary>
        /// 选择index发生更改时事件处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void listFiles_SelectedIndexChanged(object sender, EventArgs e)
        {
            // 判断是否存在项
            if (listFiles.Items.Count > 0)
            {
                btnClearFiles.Enabled = true;
                btnExportAllData.Enabled = true;
            }
            else
            {
                btnClearFiles.Enabled = false;
                btnExportAllData.Enabled = false;
            }

            // 查看是否选择了空行
            if (listFiles.SelectedItems.Count == 0)
            {
                listDetails.Items.Clear();
                btnExportData.Enabled = false;
                btnDeleteFile.Enabled = false;
                btnPlaySound.Enabled = false;
                return;
            }

            // 按键可以使用
            btnDeleteFile.Enabled = true;
            btnExportData.Enabled = true;
            btnPlaySound.Enabled = true;

            // 获取文件完整路径名称
            string name = listFiles.SelectedItems[0].SubItems[0].Text;
            string path = listFiles.SelectedItems[0].SubItems[1].Text;
            string fullFileName = path + "\\" + name;
            
            // 查看文件是否存在
            if (File.Exists(fullFileName) == false)
            {
                listDetails.Items.Clear();
                ListViewItem item = new ListViewItem("文件名");
                item.ForeColor = Color.Red;
                item.SubItems.Add("文件不存在");
                listDetails.Items.Add(item);
                return;
            }

            // 读取文件头
            BinaryReader br = new BinaryReader(new FileStream(fullFileName, FileMode.Open));
            int num = Marshal.SizeOf(typeof(WavFileDetails.WavFileHeader));
            byte[] buf = new byte[num];
            num = br.Read(buf, 0, num);
            br.Close();

            // 获取文件详细信息
            WavFileDetails details = new WavFileDetails(buf);

            // 显示信息
            listDetails_DisplayInfo(fullFileName, details);
        }

        /// <summary>
        /// 删除文件按键处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnDeleteFile_Click(object sender, EventArgs e)
        {
            // 查看是否选择了空行
            if (listFiles.SelectedItems.Count == 0)
            {
                return;
            }

            // 删除所选文件
            foreach (ListViewItem item in listFiles.SelectedItems)
            {
                listFiles.Items.Remove(item);
            }

            // 取消选中
            listFiles.SelectedItems.Clear();

            // 文件空，无法使用全局按键
            if (listFiles.Items.Count == 0)
            {
                btnClearFiles.Enabled = false;
                btnExportAllData.Enabled = false;
            }
        }

        /// <summary>
        /// 清空文件按键处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnClearFiles_Click(object sender, EventArgs e)
        {
            // 清除全部选项
            listFiles.Items.Clear();
            // 取消选中
            listFiles.SelectedItems.Clear();

            // 文件空，无法使用全局按键
            btnClearFiles.Enabled = false;
            btnExportAllData.Enabled = false;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="dataBuf"></param>
        /// <param name="fileName"></param>
        /// <returns></returns>
        private uint GetDataChunk(out byte[] dataBuf, string fileName)
        {
            int num = Marshal.SizeOf(typeof(WavFileDetails.WavRiffChunk));
            byte[] buf = new byte[num];

            // 读取文件头
            BinaryReader br = new BinaryReader(new FileStream(fileName, FileMode.Open));
            num = br.Read(buf, 0, num);

            bool findOk = true;

            // 遍历数据
            while (true)
            { 
                // 读取chunk头
                num = br.Read(buf, 0, 8);
                if (num < 8)
                {
                    findOk = false;
                    break;
                }
                num = buf[7];
                num = num * 256 + buf[6];
                num = num * 256 + buf[5];
                num = num * 256 + buf[4];

                // 判断是否是data chunk
                if ((buf[0] == 'd') && (buf[1] == 'a') && (buf[2] == 't') && (buf[3] == 'a'))
                {
                    findOk = true;
                    break;
                }
                
                br.ReadBytes(num);
            }

            if (findOk == true)
            {
                dataBuf = new byte[num];
                dataBuf = br.ReadBytes(num);
            }
            else
            {
                dataBuf = null;
                num = 0;
            }

            br.Close();

            return Convert.ToUInt32(num);
        }

        /// <summary>
        /// 导出一个文件
        /// </summary>
        /// <param name="saveDir"></param>
        /// <param name="fileName"></param>
        private void ExportOneFile(string saveDir, string fileName)
        {
            // 查看是否存在目录
            if (Directory.Exists(saveDir) == false)
            {
                // 创建目录
                Directory.CreateDirectory(saveDir);
            }
            // 获取目录和名字
            string path = Path.GetDirectoryName(fileName);
            string name = Path.GetFileNameWithoutExtension(fileName);

            // 创建目标文件
            FileStream fsDst = new FileStream(saveDir + "\\" + name + ".bin", FileMode.OpenOrCreate, FileAccess.ReadWrite);
            // 创建写入流
            BinaryWriter bw = new BinaryWriter(fsDst);
            // 读取所有chunk，找到data chunk
            byte[] buf = null;
            uint num = GetDataChunk(out buf, fileName);
            // 写入数据
            bw.Seek(0, SeekOrigin.Begin);
            bw.Write(buf);

            //关闭文件
            bw.Close();
        }

        /// <summary>
        /// 导出选中的音频数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnExportData_Click(object sender, EventArgs e)
        {
            // 查看是否选择了空行
            if (listFiles.SelectedItems.Count == 0)
            {
                return;
            }

            // 创建目录选择对话框
            FolderBrowserDialog dlg = new FolderBrowserDialog();
            dlg.SelectedPath = Directory.GetCurrentDirectory();

            // 判断是否选中
            if (dlg.ShowDialog() != DialogResult.OK)
            {
                return;
            }

            grpFileList.Enabled = false;
            grpFileOpt.Enabled = false;
            grpFileDetails.Enabled = false;

            // 导出所选文件
            foreach (ListViewItem item in listFiles.SelectedItems)
            {
                ExportOneFile(dlg.SelectedPath, item.SubItems[1].Text + "\\" + item.SubItems[0].Text);
            }

            grpFileList.Enabled = true;
            grpFileOpt.Enabled = true;
            grpFileDetails.Enabled = true;

            MessageBox.Show("导出完成！", "导出数据状态", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        /// <summary>
        /// 导出所有文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnExportAllData_Click(object sender, EventArgs e)
        {
            // 创建目录选择对话框
            FolderBrowserDialog dlg = new FolderBrowserDialog();
            dlg.SelectedPath = Directory.GetCurrentDirectory();

            // 判断是否选中
            if (dlg.ShowDialog() != DialogResult.OK)
            {
                return;
            }

            grpFileList.Enabled = false;
            grpFileOpt.Enabled = false;
            grpFileDetails.Enabled = false;

            // 导出所选文件
            foreach (ListViewItem item in listFiles.Items)
            {
                ExportOneFile(dlg.SelectedPath, item.SubItems[1].Text + "\\" + item.SubItems[0].Text);
            }

            grpFileList.Enabled = true;
            grpFileOpt.Enabled = true;
            grpFileDetails.Enabled = true;

            MessageBox.Show("导出完成！", "导出数据状态", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        /// <summary>
        /// 播放音乐
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnPlaySound_Click(object sender, EventArgs e)
        {
            // 查看是否选择了空行
            if (listFiles.SelectedItems.Count == 0)
            {
                return;
            }

            // 播放文件
            string fileName = listFiles.SelectedItems[0].SubItems[1].Text + 
                              "\\" + 
                              listFiles.SelectedItems[0].SubItems[0].Text;

            // 开始播放
            splayer.Stop();
            splayer.Dispose();
            splayer.SoundLocation = fileName;
            splayer.Play();
        }

        /// <summary>
        /// 停止播放按键处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnPlayStop_Click(object sender, EventArgs e)
        {
            splayer.Stop();
            splayer.Dispose();
        }

        /// <summary>
        /// 按键按下事件处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void listFiles_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Control && (e.KeyCode == Keys.A))
            {
                int i = 0;
                foreach (ListViewItem item in listFiles.Items)
                {
                    listFiles.Items[i++].Selected = true;
                }
            }
        }

        /// <summary>
        /// 双击行
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void listFiles_DoubleClick(object sender, EventArgs e)
        {
            // 查看是否选择了空行
            if (listFiles.SelectedItems.Count == 0)
            {
                return;
            }

            // 播放文件
            string fileName = listFiles.SelectedItems[0].SubItems[1].Text +
                              "\\" +
                              listFiles.SelectedItems[0].SubItems[0].Text;

            // 开始播放
            splayer.Stop();
            splayer.Dispose();
            splayer.SoundLocation = fileName;
            splayer.Play();
        }
    }

    /// <summary>
    /// wav文件详细信息类
    /// </summary>
    public class WavFileDetails
    {
        /// <summary>
        /// RIFF Chunk
        /// </summary>
        public struct WavRiffChunk
        {
            public uint   fileTag;          // 文件标识，"RIFF"
            public uint   fileSize;         // 文件长度，减去8字节
            public uint   fileType;         // 文件类型，"WAVE"
        }

        /// <summary>
        /// Format Chunk
        /// </summary>
        public struct WavFmtChunk
        {
            public uint   fmtTag;           // 格式标识，"fmt"
            public uint   fmtSize;          // 格式块大小
            public ushort wFormatag;        // 编码格式，包括WAVE_FORMAT_PCM，WAVEFORMAT_ADPCM等
            public ushort nChannls;         // 声道数，单声道为1，双声道为2
            public uint   nSamplesPerSec;   // 采样频率
            public uint   nAvgBytesperSec;  // 每秒的数据量
            public ushort nBlockAlign;      // 块对齐
            public ushort wBitsPerSample;   // WAVE文件的采样大小
            public ushort cbSize;           // The count in bytes of the size of extra
                                            // information(after cbSize). PCM中忽略此值
        }

        /// <summary>
        /// chunk头定义，所有的块均遵从chunk格式，包括文件头
        /// </summary>
        public struct WavChunkHeader
        {
            public uint chunkTag;
            public uint chunkSize;
        }

        /// <summary>
        /// 文件头结构
        /// </summary>
        public struct WavFileHeader
        {
            // RIFF
            public WavRiffChunk riff;
            // Format
            public WavFmtChunk  fmt;
        };

        // 文件头
        public WavFileHeader header = new WavFileHeader();

        /// <summary>  
        /// 由结构体转换为byte数组  
        /// </summary>  
        public static byte[] StructureToByte<T>(T structure)
        {
            int size = Marshal.SizeOf(typeof(T));
            byte[] buffer = new byte[size];
            IntPtr bufferIntPtr = Marshal.AllocHGlobal(size);
            try
            {
                Marshal.StructureToPtr(structure, bufferIntPtr, true);
                Marshal.Copy(bufferIntPtr, buffer, 0, size);
            }
            finally
            {
                Marshal.FreeHGlobal(bufferIntPtr);
            }
            return buffer;
        }

        /// <summary>  
        /// 由byte数组转换为结构体  
        /// </summary>  
        public static T ByteToStructure<T>(byte[] dataBuffer)
        {
            object structure = null;
            int size = Marshal.SizeOf(typeof(T));
            IntPtr allocIntPtr = Marshal.AllocHGlobal(size);
            try
            {
                Marshal.Copy(dataBuffer, 0, allocIntPtr, size);
                structure = Marshal.PtrToStructure(allocIntPtr, typeof(T));
            }
            finally
            {
                Marshal.FreeHGlobal(allocIntPtr);
            }
            return (T)structure;
        }

        /// <summary>
        /// 结构体转byte数组
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="structure"></param>
        /// <returns></returns>
        public byte[] GetByte<T>(T structure)
        {
            MemoryStream ms = new MemoryStream();
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Serialize(ms, structure);
            byte[] result = ms.ToArray();
            ms.Close();
            ms.Dispose();
            return result;
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="buf"></param>
        public WavFileDetails(byte[] buf)
        {
            header = ByteToStructure<WavFileHeader>(buf);
        }
    }
}
